-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Add Client Metadata Update Support. #1708
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
JAVA-5854
JAVA-5854
dependencies { | ||
testImplementation(libs.assertj) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AssertJ has been used in this PR and added to test-base as it is a useful library that could be shared across all modules.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A decision to use a new assertion library seems like something that should be debated a bit more widely, rather than being brought in as part of a PR like this one. How important do you think it is to the assertions required for this PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that adopting a new assertion or testing approach should generally be a broader team decision. This isn’t a critical for this PR, so I can remove it from this PR. AssertJ has already been used in the Kotlin coroutine module (reference) and is present in the toml file, though it’s not widely used in the codebase. This PR made the library visible to all modules, based on its usefulness in the Hibernate repository, which was the motivation for extending its usage further.
For now, I will revert this change and we can revisit the discussion as a team.
JAVA-5854
JAVA-5854
JAVA-5854
JAVA-5854
JAVA-5854
JAVA-5870
JAVA-5870
JAVA-5870
JAVA-5870
JAVA-5870
JAVA-5870
JAVA-5870
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initial feedback
* @param mongoDriverInformation the driver information to append to the existing metadata | ||
* @since 5.6 | ||
*/ | ||
void updateMetadata(MongoDriverInformation mongoDriverInformation); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the argument for putting the method on MongoClient rather than MongoCluster?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just conceptually, MongoCluster
acts as a view with overridden configurations (e.g., codec registry, read preference) and doesn’t own the client’s lifecycle or global state. In my opinion, placing updateMetadata
on MongoCluster
raises the question: does it update metadata for just that instance or for all instances? That behavior would require clear documentation.
MongoClient
already handles driver-wide operations (e.g., close), so updateMetadata
aligns with its role.
* <p>This class is not part of the public API and may be removed or changed at any time</p> | ||
*/ | ||
public class ClientMetadata { | ||
private volatile BsonDocument clientMetadataBsonDocument; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't thing that a volatile fields is enough. If append
is called concurrently by multiple threads, then one of the updates to metadata will likely be lost.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right - volatile alone isn't enough for concurrent updates. I initially didn’t add locking because I expected concurrent metadata updates to be extremely rare in practice, but I’ve now added a lock to ensure consistency. Let me know what you think.
UPD: discussed offline. Removed clone()
from updateMetadata(...)
for clarify: 8ade58b
JAVA-5870
JAVA-5870
JAVA-5870
|
||
/** | ||
* Represents metadata of the current MongoClient. | ||
* | ||
* <p>This class is not part of the public API and may be removed or changed at any time</p> | ||
*/ | ||
public class ClientMetadata { | ||
private final ReentrantLock updateLock = new ReentrantLock(); | ||
private volatile BsonDocument clientMetadataBsonDocument; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you remove the volatile
, then I think you need to use the updateLock.readLock()
in getBsonDocument
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I intended to use the readLock(
) here. Thanks for catching that - updated accordingly!
@@ -36,7 +36,7 @@ public class ClientMetadata { | |||
private BsonDocument clientMetadataBsonDocument; | |||
|
|||
public ClientMetadata(@Nullable final String applicationName, final MongoDriverInformation mongoDriverInformation) { | |||
withLock(readWriteLock.writeLock(), () -> { | |||
withLock(readWriteLock.readLock(), () -> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why switching to a readLock()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, that was an oversight on my part - I meant to apply the change in getBsonDocument
. Fixed here:
Fix readLock issue.
I switched from |
.gitmodules
Outdated
url = https://github.com/vbabanin/specifications | ||
branch = DRIVERS-2985 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A link to the fork was added to connect the specification PR under review with this PR, letting unified tests to run. This should be removed once the specification PR is merged and before merging this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reverted back: 31ef18e
Synced with https://github.com/mongodb/specifications upstream master: 38ba5e6
# Conflicts: # driver-core/src/test/resources/specifications # driver-core/src/test/unit/com/mongodb/internal/connection/MultiServerClusterSpecification.groovy
# Conflicts: # driver-core/src/main/com/mongodb/internal/connection/BaseCluster.java
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure why the changes of SDAM/Connection/Monitoring is part of this PR? The client metadata aspect LGTM but didn't review the SDAM changes
@@ -269,7 +270,7 @@ private boolean handleReplicaSetMemberChanged(final ServerDescription newDescrip | |||
} | |||
|
|||
if (isStalePrimary(newDescription)) { | |||
invalidatePotentialPrimary(newDescription); | |||
invalidatePotentialPrimary(newDescription, new MongoStalePrimaryException("Primary marked stale due to electionId/setVersion mismatch")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a test asserting this exception?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why it show up empty in the diff?
dependencies { | ||
testImplementation(libs.assertj) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A decision to use a new assertion library seems like something that should be debated a bit more widely, rather than being brought in as part of a PR like this one. How important do you think it is to the assertions required for this PR?
import java.util.concurrent.locks.ReentrantReadWriteLock; | ||
|
||
import static com.mongodb.internal.Locks.withLock; | ||
import static com.mongodb.internal.connection.ClientMetadataHelper.createClientMetadataDocument; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that we have a proper ClientMetadata
class that is meant to be instantiated, it seems like we no longer need a separate ClientMetadataHelper
class, instead moving the methods of that class into this one.
* <p> | ||
* Platform is appended separately to the existing platform if it does not exceed {@value MAXIMUM_CLIENT_METADATA_ENCODED_SIZE} bytes. | ||
*/ | ||
public static BsonDocument updateClientMetadataDocument(final BsonDocument clientMetadataDocument, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's consider a slightly different algorithm for updating client metadata that shared more logic with createClientMetadataDocument
.
Now that we have a ClientMetadata
class that is meant to be instantiated, we can store more than just the resulting BsonDocument
representing the metadata. We can also store the data from which it was created: the application name and the MongoDriverInformation
. If we have that, we can share all the create logic by first merging the original MongoDriverInformation
with the appended one, and then calling createClientMetadataDocument
. We might need a new MongoDriverInformation#merge
method, or it could be added to the MongoDriverInformation.Builder
, but otherwise it reduces the code complexity a bit.
What do you think?
import java.util.concurrent.locks.ReentrantReadWriteLock; | ||
|
||
import static com.mongodb.internal.Locks.withLock; | ||
import static com.mongodb.internal.connection.ClientMetadataHelper.createClientMetadataDocument; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's also surprising that ClientMetadataHelper.createClientMetadataDocument
is called directly in multiple places, e.g. com.mongodb.MongoClient
, instead of just getting the ClientMetadata
from the cluster or delegate. Looks like it's done just to log the metadata. Could we simplify that?
After merging main into this branch, some diffs (SDAM-related changes) are appearing, even though they’re already present on main and the total number of line additions hasn’t changed. I’m looking into why this is happening. |
Description
This PR updates the Java sync and reactive-streams driver to support dynamic client metadata updates, enabling wrapping libraries and integrations to append metadata about themselves when they do not instantiate the
MongoClient
directly.JAVA-5870
NOTE BEFORE MERGING:
Remove a submodule link to a fork: #1708 (comment)